/**
 * 审计日志切面实现 - AOP切面拦截器，负责记录系统操作审计日志
 *
 * 开发人员：邢珈晟
 * 开发时间：2025-10-20 至 2025-10-25
 * 模块职责：实现AOP切面逻辑，拦截带有@AuditLog注解的方法，记录详细的操作审计信息
 *
 * 技术要点：
 * - AOP环绕通知实现方法拦截
 * - 多租户用户信息和企业信息收集
 * - 请求上下文信息采集和日志记录
 * - 操作耗时统计和性能监控
 * - 异步日志记录避免影响业务性能
 *
 * 开发故事：负责审计日志切面的设计和实现，与审计日志注解深度配合，为系统提供完整的安全审计和操作追踪能力。
 */
package com.pbl.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pbl.entity.Enterprise;
import com.pbl.entity.OperationLog;
import com.pbl.entity.User;
import com.pbl.repository.OperationLogRepository;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class AuditLogAspect {

    private final OperationLogRepository operationLogRepository;
    private final HttpServletRequest request;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public AuditLogAspect(OperationLogRepository operationLogRepository, HttpServletRequest request) {
        this.operationLogRepository = operationLogRepository;
        this.request = request;
    }

    @Around("@annotation(com.pbl.aspect.AuditLog)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        AuditLog al = method.getAnnotation(AuditLog.class);

        Object result = null;
        Exception ex = null;
        long start = System.currentTimeMillis();
        try {
            result = pjp.proceed();
            return result;
        } catch (Exception e) {
            ex = e;
            throw e;
        } finally {
            long cost = System.currentTimeMillis() - start;
            saveLog(al, pjp.getArgs(), result, ex, cost);
        }
    }

    private void saveLog(AuditLog al, Object[] args, Object result, Exception ex, long cost) {
        try {
            OperationLog log = new OperationLog();
            // 简化：从Header中读取企业与用户（生产建议从JWT claims中解析）
            String enterpriseId = request.getHeader("X-Enterprise-Id");
            String userId = request.getHeader("X-User-Id");
            
            // 如果没有企业ID头，使用默认值1（Demo Enterprise）
            if (enterpriseId == null) {
                enterpriseId = "1";
            }
            
            Enterprise ent = new Enterprise();
            ent.setId(Long.parseLong(enterpriseId));
            log.setEnterprise(ent);
            if (userId != null) {
                User u = new User();
                u.setId(Long.parseLong(userId));
                log.setUser(u);
            }
            log.setAction(al.action());
            log.setResourceType(al.resource());
            log.setIp(request.getRemoteAddr());
            log.setUserAgent(request.getHeader("User-Agent"));

            Map<String, Object> detail = new HashMap<>();
            detail.put("path", request.getRequestURI());
            detail.put("method", request.getMethod());
            detail.put("args", args);
            detail.put("costMs", cost);
            if (ex != null) {
                detail.put("error", ex.getMessage());
            }
            log.setDetail(objectMapper.writeValueAsString(detail));
            operationLogRepository.save(log);
        } catch (Exception ignored) {
        }
    }
}
